#include <QApplication>
#include <QGridLayout>
#include <QLabel>
#include <QWidget>
#include <QKeyEvent>
#include <QList>
#include <QPoint>
#include <QMessageBox>


class Game2048 : public QWidget {
public:
    Game2048(QWidget *parent = nullptr) : QWidget(parent) {
        setWindowTitle("2048");

        gridLayout = new QGridLayout(this);
        gridLayout->setSpacing(5);

        initializeBoard();

        setLayout(gridLayout);
        setFocusPolicy(Qt::StrongFocus);
    }

protected:
    void keyPressEvent(QKeyEvent *event) override {
        if (event->key() == Qt::Key_Left) {
            moveTilesLeft();
        } else if (event->key() == Qt::Key_Right) {
            moveTilesRight();
        } else if (event->key() == Qt::Key_Up) {
            moveTilesUp();
        } else if (event->key() == Qt::Key_Down) {
            moveTilesDown();
        }

        if (!isGameOver()) {
            addRandomTile();
        } else {
            // Handle game over
            // For simplicity, just show a message box
            QMessageBox::information(this, "Game Over", "Game Over!");
        }

        updateUI();
    }

private:
    void initializeBoard() {
        // Initialize the board with zeros
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                board[i][j] = 0;
            }
        }

        // Add two random tiles to start the game
        addRandomTile();
        addRandomTile();
    }

    void addRandomTile() {
        // Find empty tiles
        QList<QPoint> emptyTiles;
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                if (board[i][j] == 0) {
                    emptyTiles.append(QPoint(i, j));
                }
            }
        }

        // Add a random tile (either 2 or 4) to a random empty tile
        if (!emptyTiles.isEmpty()) {
            QPoint randomEmptyTile = emptyTiles[rand() % emptyTiles.size()];
            board[randomEmptyTile.x()][randomEmptyTile.y()] = (rand() % 2 + 1) * 2;
        }
    }

    void moveTilesLeft() {
        for (int i = 0; i < 4; ++i) {
            int k = 0;
            for (int j = 0; j < 4; ++j) {
                if (board[i][j] != 0) {
                    if (k != j) {
                        board[i][k] = board[i][j];
                        board[i][j] = 0;
                    }
                    k++;
                }
            }
            for (int j = 0; j < 3; ++j) {
                if (board[i][j] == board[i][j + 1]) {
                    board[i][j] *= 2;
                    board[i][j + 1] = 0;
                }
            }
            k = 0;
            for (int j = 0; j < 4; ++j) {
                if (board[i][j] != 0) {
                    if (k != j) {
                        board[i][k] = board[i][j];
                        board[i][j] = 0;
                    }
                    k++;
                }
            }
        }
    }

    void moveTilesRight() {
        for (int i = 0; i < 4; ++i) {
            int k = 3;
            for (int j = 3; j >= 0; --j) {
                if (board[i][j] != 0) {
                    if (k != j) {
                        board[i][k] = board[i][j];
                        board[i][j] = 0;
                    }
                    k--;
                }
            }
            for (int j = 3; j > 0; --j) {
                if (board[i][j] == board[i][j - 1]) {
                    board[i][j] *= 2;
                    board[i][j - 1] = 0;
                }
            }
            k = 3;
            for (int j = 3; j >= 0; --j) {
                if (board[i][j] != 0) {
                    if (k != j) {
                        board[i][k] = board[i][j];
                        board[i][j] = 0;
                    }
                    k--;
                }
            }
        }
    }


    void moveTilesUp() {
        // Transpose the board
        int transposedBoard[4][4];
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                transposedBoard[i][j] = board[j][i];
            }
        }

        // Move tiles up (similar to moveTilesLeft)
        for (int i = 0; i < 4; ++i) {
            int k = 0;
            for (int j = 0; j < 4; ++j) {
                if (transposedBoard[i][j] != 0) {
                    if (k != j) {
                        transposedBoard[i][k] = transposedBoard[i][j];
                        transposedBoard[i][j] = 0;
                    }
                    k++;
                }
            }
            for (int j = 0; j < 3; ++j) {
                if (transposedBoard[i][j] == transposedBoard[i][j + 1]) {
                    transposedBoard[i][j] *= 2;
                    transposedBoard[i][j + 1] = 0;
                }
            }
            k = 0;
            for (int j = 0; j < 4; ++j) {
                if (transposedBoard[i][j] != 0) {
                    if (k != j) {
                        transposedBoard[i][k] = transposedBoard[i][j];
                        transposedBoard[i][j] = 0;
                    }
                    k++;
                }
            }
        }

        // Transpose the board back
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                board[i][j] = transposedBoard[j][i];
            }
        }
    }


    void moveTilesDown() {
        // Transpose the board
        int transposedBoard[4][4];
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                transposedBoard[i][j] = board[j][i];
            }
        }

        // Move tiles down (similar to moveTilesRight)
        for (int i = 0; i < 4; ++i) {
            int k = 3;
            for (int j = 3; j >= 0; --j) {
                if (transposedBoard[i][j] != 0) {
                    if (k != j) {
                        transposedBoard[i][k] = transposedBoard[i][j];
                        transposedBoard[i][j] = 0;
                    }
                    k--;
                }
            }
            for (int j = 3; j > 0; --j) {
                if (transposedBoard[i][j] == transposedBoard[i][j - 1]) {
                    transposedBoard[i][j] *= 2;
                    transposedBoard[i][j - 1] = 0;
                }
            }
            k = 3;
            for (int j = 3; j >= 0; --j) {
                if (transposedBoard[i][j] != 0) {
                    if (k != j) {
                        transposedBoard[i][k] = transposedBoard[i][j];
                        transposedBoard[i][j] = 0;
                    }
                    k--;
                }
            }
        }

        // Transpose the board back
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                board[i][j] = transposedBoard[j][i];
            }
        }
    }



    bool isGameOver() const {
        // Check if there are any empty tiles
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                if (board[i][j] == 0) {
                    return false;
                }
            }
        }

        // Check if there are any adjacent tiles with the same value
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                if ((j + 1 < 4 && board[i][j] == board[i][j + 1]) ||
                    (i + 1 < 4 && board[i][j] == board[i + 1][j])) {
                    return false;
                }
            }
        }

        return true; // Game over
    }


    void updateUI() {
        // Clear the current UI
        QLayoutItem *child;
        while ((child = gridLayout->takeAt(0)) != nullptr) {
            delete child->widget();
            delete child;
        }

        // Update UI with current board state
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                QLabel *label = createTileLabel(board[i][j]);
                gridLayout->addWidget(label, i, j);
            }
        }
    }


    QLabel* createTileLabel(int value) {
        QLabel* label = new QLabel(QString::number(value));
        label->setAlignment(Qt::AlignCenter);
        label->setStyleSheet("QLabel { background-color: lightgray; border-radius: 5px; font-size: 24px;}");
        return label;
    }

    QGridLayout* gridLayout;
    int board[4][4]; // Represents the game board
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    Game2048 game;
    game.show();

    return app.exec();
}
